/**
 * CommandProcessor
 * 
 * Version 1.0
 *
 * Nov 29th, 2009
 * 
 * Copyright (c) 2009 Oksana Yaremchuk
 */

import java.util.Stack;
import java.io.Serializable;
import java.util.EmptyStackException;
import java.util.Iterator;


/**
 * CommandProcessor contains all command objects.
 * Schedules the execution of commands.
 * Uses singleton to insure only one instance.
 * @author Oksana Yaremchuk 
 */
public class CommandProcessor  implements Serializable {

	/**serialVersionUID needed for managing Serializable interface*/
	private static final long serialVersionUID = 1L;
	
    /** the stack which contains the commands which can be undone */
	private Stack<Command> undoStack;
	
	/** the stack which contains the commands which can be redone */
	private Stack<Command> redoStack;
	
	private static CommandProcessor commandProcessor;
	

	/**
	 * Constructor which creates a new redo and undo stack
	 */
	private CommandProcessor() {
	    undoStack = new Stack<Command>();
	    redoStack = new Stack<Command>();
	}
	
	/**
	 * return the singleton command processor
	 * @return return a reference to the command processor
	 */
	public static CommandProcessor makeCommandProcessor(){
		if (commandProcessor == null){
			commandProcessor = new CommandProcessor();
		}
		return commandProcessor;
	}
	
	/**
	 * checks for empty in redo stack
	 * @return TRUE, if stack is empty
	 */
	public boolean isRedoEmpty()
    {
    	return redoStack.size() == 0;
    }
	
	/**
	 * checks for empty in undo stack
	 * @return	TRUE< if stack empty
	 */
	public boolean isUndoEmpty()
    {
    	return undoStack.size() == 0;
    }
	
	/**
	 * pops the redo stack
	 * @return	the top command
	 */
	public Command popRedo()
	{
		return redoStack.pop();
	}
	
	/**
	 * pops the undo stack
	 * @return the command
	 */
	public Command popUndo()
	{
		return undoStack.pop();
	}
	
	/**
	 * peek the top of the redo stack
	 * @return	the command at the top of the stack
	 */
	public Command peekRedo()
    {
    	if(redoStack.empty())
    	{
    		throw new EmptyStackException();
    	}
    	
    	return redoStack.peek();
    }
	
	/**
	 * peek at the top of the undo stack
	 * @return	the command at the top of the stack
	 */
	public Command peekUndo()
    {
    	if(undoStack.empty())
    	{
    		throw new EmptyStackException();
    	}
    	
    	return undoStack.peek();
    }
	
	/**
	 * push a Command to the redo stack
	 * @param c	a command to push
	 * @return	the same command
	 */
	public Command pushRedo(Command c)
    {
    	return redoStack.push(c);
    }
	
	/**
	 * push a Command to the undo stack
	 * @param c	a command to push
	 * @return	the same command
	 */
	public Command pushUndo(Command c)
    {
    	return undoStack.push(c);
    }
	
	/**
	 * Administrative method which clears the redo stack
	 */
	public void clearRedo()
	{
		if (!isRedoEmpty()) 
		{
			redoStack.clear();
	       
	    }
	}
	
	/**
	 * Administrative method which clears the undo stack
	 */
	public void clearUndo()
	{
		if (!isUndoEmpty()) 
		{
			undoStack.clear();
	           
	    } 		
	}
	
	/**
	 * Undo the previous command pushed on the stack
	 * @throws CommandProcessorException if the undo stack is empty
	 * @throws CommandException
	 */
	public void undo() throws CommandException, CommandProcessorException
	{
	    if (!isUndoEmpty()) {
	        Command c = popUndo();
	        c.undo();
	        pushRedo(c);	        
	    } else {
	        throw new CommandProcessorException("There is nothing to undo.");
	    }
	
	}

	/**
	 * Redo the last command pushed into the redo stack
	 * @throws CommandProcessorException if the redo stack is empty
	 * @throws CommandException
	 */
	public void redo() throws CommandException, CommandProcessorException
	{
	    if (!isRedoEmpty()) 
	    {
	        Command c = popRedo();
	        c.execute();   	
	        pushUndo(c);
	    } else {
	       throw new CommandProcessorException( "There is nothing to redo.");
	    }
	}
	
	/**
	 * Execute the given command
	 * @param cmmd  the command to be executed
	 * @throws CommandException
	 */
	public void execute(Command cmmd) throws CommandException
	{
//		/**Get the new value*/
//		String str1 = cmmd.toString();
//	   
//		if(!isUndoEmpty())
//		{
//
//			/**peek command previously pushed into undoStack*/
//			String str2 = peekUndo().toString();
//			
//			
//			/**If a new command is the same as the previous one than we will not execute it*/
//			if(str1.equals(str2))
//			{
//				
//			    return;
//			}
//		}
		
		
		cmmd.execute();
		
		pushUndo(cmmd);
		this.clearRedo();			

	}
	
	/**
	* @return
    * @see java.util.Collection#iterator()
    */
   public Iterator<Command> iteratorRedo() 
   {
	  return redoStack.iterator();
   }
	   
   /**
	* @return
	* @see java.util.Collection#iterator()
	*/
   public Iterator<Command> iteratorUndo() 
   {
      return undoStack.iterator();
   }

	
	/**
	 * toString() method that will iterate over redoStack and undoStack and bring
	 * the content to the screen 
	 */
	public String toString()
	{
		Iterator iterRedo=redoStack.iterator();
		Iterator iterUndo=undoStack.iterator();
		
		String strRedo="";
		String strUndo="";
		
		/**Iterate over redoStack*/
		while(iterRedo.hasNext())
		{
			
			Command cmmd = (Command)iterRedo.next();
			
			/**getClass() will get the class of the object at the run time. So toString() method of that class
			 * will be called.
			*/
		    strRedo+= cmmd.getClass().toString()+ "\n";
		}
		
		/**Iterate over undoStack*/
		while(iterUndo.hasNext())
		{
			Command cmmd = (Command)iterUndo.next();
			
		    strUndo+= cmmd.getClass().toString()+ "\n";
		}
		
		/**Return the content of both stack to the screen*/
		return "Inside redoStack: \n" +strRedo+ "\n Inside Undostack: \n" +strUndo;
	}
	
}//end of class
